﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Astronergy.MES.App.Library.Logger
{
    public class Logger : IDisposable
    {
        private static readonly int _keepLogDayCount = 15;

        private static int _lineCount;

        private static readonly int _lineMaxCount = 0x3d090;//2500

        private static Queue<LogMsg> _msgs;

        private static string _path;

        private static bool _state;

        private static DateTime _timeSign;

        private static EnumLogType _type;

        private static StreamWriter _writer;

        private readonly bool _bOutputCount = false;

        private readonly string _format = "yyyy-MM-dd HH:mm:ss:fff";

        private long _count;

        public Logger(string path, EnumLogType type)
        {
            if (_msgs == null)
            {
                _state = true;
                _path = path;
                _type = type;
                _msgs = new Queue<LogMsg>();
                var thread = new Thread(Work) { IsBackground = true };
                thread.Start();
            }
        }

        public void Dispose()
        {
            _state = false;
        }

        private void FileClose()
        {
            if (_writer == null) return;
            _writer.Flush();
            _writer.Close();
            _writer.Dispose();
            _writer = null;
        }

        private void FileOpen()
        {
            _lineCount = 0;
            _writer = new StreamWriter(GetFileName(), true, Encoding.UTF8);
        }

        private void FileWrite(LogMsg msg)
        {
            try
            {
                if (_writer == null)
                {
                    FileOpen();
                }

                if (_writer != null)
                {
                    if ((DateTime.Now >= _timeSign) || _lineCount > _lineMaxCount)
                    {
                        FileClose();
                        try
                        {
                            var fileSn = 0;
                            for (var str = _path + GetFilenameBeforeDays(0, _keepLogDayCount);
                                File.Exists(str);
                                str = _path + GetFilenameBeforeDays(fileSn, _keepLogDayCount))
                            {
                                File.Delete(str);
                                fileSn++;
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.Out.Write(exception);
                        }
                        FileOpen();
                    }
                    _lineCount++;
                    _writer.Write(msg.DateTime.ToString(_format));
                    _writer.Write(' ');
                    if (msg.Type == EunmMsgType.Unknown)
                    {
                        _writer.Write(msg.Category);
                    }
                    else
                    {
                        _writer.Write(msg.Type);
                    }
                    _writer.Write(' ');
                    _writer.WriteLine(msg.Text);
                    _writer.Flush();
                }
            }
            catch (Exception exception)
            {
                Console.Out.Write(exception);
            }
        }

        private string GetFileName()
        {
            var num = 0;
            var now = DateTime.Now;
            var format = "";

        Label_OO0E:
            switch (_type)
            {
                case EnumLogType.Daily:
                    _timeSign = new DateTime(now.Year, now.Month, now.Day);
                    _timeSign = _timeSign.AddDays(1.0);
                    format = "yyyyMMdd";
                    break;
                case EnumLogType.Weekly:
                    _timeSign = new DateTime(now.Year, now.Month, now.Day);
                    _timeSign = _timeSign.AddDays(7.0);
                    format = "yyyyMMdd";
                    break;
                case EnumLogType.Monthly:
                    _timeSign = new DateTime(now.Year, now.Month, 1);
                    _timeSign = _timeSign.AddMonths(1);
                    format = "yyyyMM";
                    break;
                case EnumLogType.Annually:
                    _timeSign = new DateTime(now.Year, 1, 1);
                    _timeSign = _timeSign.AddYears(1);
                    format = "yyyy";
                    break;
            }
            if (File.Exists(_path + now.ToString(format) + string.Format("-{0}.log", num)))
            {
                num++;
                goto Label_OO0E;
            }
            return _path + now.ToString(format) + string.Format("-{0}.log", num);
        }

        private string GetFilenameBeforeDays(int fileSn, int dayCount)
        {
            var time = DateTime.Now - TimeSpan.FromDays(dayCount);
            var format = "";
            switch (_type)
            {
                case EnumLogType.Daily:
                    format = "yyyyMMdd";
                    break;
                case EnumLogType.Weekly:
                    format = "yyyyMMdd";
                    break;
                case EnumLogType.Monthly:
                    format = "yyyyMM";
                    break;
                case EnumLogType.Annually:
                    format = "yyyy";
                    break;
            }
            return time.ToString(format) + string.Format("-{0}.log", fileSn);
        }

        private void Work()
        {
        Label_0000:
            while (_msgs.Count > 0)
            {
                LogMsg msg = null;
                lock (_msgs)
                {
                    if (_msgs.Count > 0)
                    {
                        msg = _msgs.Dequeue();
                        _count -= 1L;
                    }

                    if (msg != null)
                    {
                        if (_bOutputCount)
                        {
                            msg.Text = msg.Text + string.Format("{0}", _count);
                        }
                        FileWrite(msg);
                    }
                }
            }

            if (_state)
            {
                Thread.Sleep(1);
                goto Label_0000;
            }
            FileClose();
        }

        public void Write(LogMsg msg)
        {
            if (msg != null)
            {
                lock (_msgs)
                {
                    _count += 1L;
                    _msgs.Enqueue(msg);
                }
            }
        }

        public void Write(string text, EunmMsgType type)
        {
            Write(new LogMsg(text, type));
        }

        public void Write(string text, string category)
        {
            Write(new LogMsg(text, category));
        }
    }
}
